package ms.aurora.util;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;

public class Reflection {
    private final Class<?> instanceClass;

    public Reflection(Class<?> instanceClass) {
        this.instanceClass = instanceClass;
    }

    public Collection<Method> getMethods(Predicate<Method> methodPred) {
        return Collections2.filter(getMethods(), methodPred);
    }

    public Collection<Method> getMethods() {
        return Arrays.asList(instanceClass.getDeclaredMethods());
    }

    public Collection<Field> getFields(Predicate<Field> fieldPred) {
        return Collections2.filter(getFields(), fieldPred);
    }

    public Collection<Field> getFields() {
        return Arrays.asList(instanceClass.getDeclaredFields());
    }

    public static class Filters {
        public static final Predicate<Field> fieldAnnotatedWith(
                final Class<? extends Annotation> annotation) {
            return new Predicate<Field>() {
                @Override
                public boolean apply(Field arg0) {
                    return arg0.isAnnotationPresent(annotation);
                }
            };
        }

        public static final Predicate<Method> methodAnnotatedWith(
                final Class<? extends Annotation> annotation) {
            return new Predicate<Method>() {
                @Override
                public boolean apply(Method arg0) {
                    return arg0.isAnnotationPresent(annotation);
                }
            };
        }

        public static final Predicate<Field> fieldNamed(final String field) {
            return new Predicate<Field>() {
                @Override
                public boolean apply(Field arg0) {
                    return arg0.getName().equals(field);
                }
            };
        }

        public static final Predicate<Method> methodNamed(final String method) {
            return new Predicate<Method>() {
                @Override
                public boolean apply(Method arg0) {
                    return arg0.getName().equals(method);
                }
            };
        }
    }

    public static class Converters {
        public static final Function<Field, Annotation> fieldToAnnotation(
                final Class<? extends Annotation> annotation) {
            return new Function<Field, Annotation>() {
                @Override
                public Annotation apply(Field arg0) {
                    return arg0.getAnnotation(annotation);
                }
            };
        }
    }
}
